package org.eclipse.epsilon.emc.jdt2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.epsilon.common.util.StringProperties;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter;
import org.eclipse.epsilon.eol.models.Model;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;

/**
 * This is the JDT model class.
 * 
 * @author Cheng Yun
 * 
 */
public class JdtModel extends Model {
	public static final String JAVA_PROJECT = "JavaProject";
	public static final String PACKAGE_DEC = "PackageDeclaration";
	public static final String TYPE_DEC = "TypeDeclaration";
	public static final String METHOD_DEC = "MethodDeclaration";
	public static final String CONSTRUCTOR_DEC = "ConstructorDeclaration";
	public static final String FIELD = "FieldDeclaration";

	private List<String> supportedTypes = Arrays.asList("JavaProject",
			"Package", "ConstructorDeclaration","FieldDeclaration");
	private List<IJavaProject> javaProjects = new ArrayList<IJavaProject>();
	private ASTReflection astModel;

	private List<String> list;
	private List<String> list2;

	@Override
	public void load() throws EolModelLoadingException {
		getJavaProject(null);
		try {
			astModel = new ReflectiveASTVisitor(javaProjects,
					true);
		} catch (JavaModelException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public String foo1() {
		// list to collect all type declarations
		list = new ArrayList<String>();
		// list to collect all matched results
		list2 = new ArrayList<String>();

		// find the source Java project
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		IProject[] projects = root.getProjects();
		IJavaProject javaProject = JavaCore.create(projects[0]);
		IJavaProject[] javaProjects = { javaProject };

		// create search pattern: search all declarations of types
		SearchPattern pattern = SearchPattern.createPattern("*",
				IJavaSearchConstants.TYPE,
				IJavaSearchConstants.DECLARATIONS,
				SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE);
		// create search scope with the packages above and set only search for
		// source folders
		IJavaSearchScope scope = SearchEngine.createJavaSearchScope(
				javaProjects, IJavaSearchScope.SOURCES);
		// create search requestor
		SearchRequestor requestor = new SearchRequestor() {
			@Override
			public void acceptSearchMatch(SearchMatch match) {
				// Save the SearchMatch somewhere
				Object object = match.getElement();
				IType temp = null;
				if (object instanceof IType) {
					// if the result is a type declaration
					temp = (IType) object;
					list.add(temp.getElementName() + ":"
							+ temp.getElementType());
				}
				list2.add(object.toString());
			}
		};
		// create the search engine and start to search
		SearchEngine engine = new SearchEngine();
		try {
			engine.search(pattern, new SearchParticipant[] { SearchEngine
					.getDefaultSearchParticipant() }, scope, requestor, null);
		} catch (CoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String result = list.size() + "\n" + list2.size() + "\n";
		for (String s : list) {
			result = result + s + "\n";
		}
		return result;
	}

	@Override
	public void load(StringProperties properties, String basePath)
			throws EolModelLoadingException {
		super.load(properties, basePath);
		String projects = properties.getProperty(JdtModelConfig.PROJECTS);
		String[] projectsSelected = null;
		if (projects.length() != 0) {
			projectsSelected = projects.split(",");
		}
		getJavaProject(projectsSelected);

		// bindings switchW
		boolean ifBindings = Boolean.parseBoolean(properties
				.getProperty(JdtModelConfig.BINDINGS));

		// select models
		try {
			String str = properties.getProperty(JdtModelConfig.MODELS);
			if (str != "") {
				int temp = Integer.parseInt(str);
				switch (temp) {
				case JdtModelConfig.VIRTUAL:
					astModel = new ReflectiveASTVisitor(javaProjects,
							ifBindings);
					break;
				case JdtModelConfig.PHYSICAL:
					astModel = new ASTModel(javaProjects, ifBindings);
					break;
				case JdtModelConfig.PHYSICAL_GC:
					// TODO not implemented
					astModel = new ASTModel(javaProjects, ifBindings);
					break;
				default:
					astModel = new ReflectiveASTVisitor(javaProjects,
							ifBindings);
					break;
				}
			} else {
				astModel = new ReflectiveASTVisitor(javaProjects, ifBindings);
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Object getEnumerationValue(String enumeration, String label)
			throws EolEnumerationValueNotFoundException {
		return null;
	}

	@Override
	public Collection<?> allContents() {
		try {
			return astModel.getAllOfKind("ComopilationUnit");
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return new ArrayList<Object>();
	}

	@Override
	public Collection<?> getAllOfType(String type)
			throws EolModelElementTypeNotFoundException {
		System.out.println("GET ALL OF TYPE CALLED!");
		
		
		// get java projects
		if ("JavaProject".equals(type)) {
			return javaProjects;
		}

		// get packages
		if ("Package".equals(type)) {
			List<IPackageFragment> packages = new QuickList(PACKAGE_DEC, astModel,
					javaProjects);
			// try {
			// // get packages
			// packages.addAll(JdtReader.getIPackageFragments(javaProjects,
			// true));
			// } catch (CoreException e) {
			// e.printStackTrace();
			// }
			return packages;
		}

		// if(astModel == null)
		// System.out.println("ASTMODEL NULL");
		try {
			return astModel.getAllOfType(type);
		} catch (JavaModelException e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<?> getAllOfKind(String type)
			throws EolModelElementTypeNotFoundException {
		System.out.println("GET ALL OF KIND CALLED");
		
		// get java projects
		if ("JavaProject".equals(type)) {
			return javaProjects;
		}

		// get packages
		if ("Package".equals(type)) {
			// System.out.println("Get all packages");
			List<IPackageFragment> packages = new QuickList(PACKAGE_DEC, astModel,
					javaProjects);
			return packages;
		}

		if ("TypeDeclaration".equals(type)) {
			// System.out.println("type declaration");
			List<TypeDeclaration> types = new QuickList(TYPE_DEC, astModel,
					javaProjects);
			return types;
		}

		if ("MethodDeclaration".equals(type)) {
			// System.out.println("method declaration");
			List<MethodDeclaration> methods = new QuickList(METHOD_DEC, astModel,
					javaProjects);
			return methods;
		}

		if ("ConstructorDeclaration".equals(type)) {
			// System.out.println("constructor declaration");
			List<MethodDeclaration> constructors = new QuickList(CONSTRUCTOR_DEC, astModel,
					javaProjects);
			return constructors;
		}
		
		if ("FieldDeclaration".equals(type)){
			List<FieldDeclaration> fields = new QuickList(FIELD,astModel,javaProjects);
			return fields;
		}

		try {
			return astModel.getAllOfKind(type);
		} catch (JavaModelException e) {
			throw new RuntimeException(e);
		}

		// return getAllOfType(type);
	}

	@Override
	public String getTypeNameOf(Object instance) {
		if (instance instanceof IJavaProject) {
			return IJavaProject.class.toString();
		}

		if (instance instanceof IPackageFragment) {
			return IJavaProject.class.toString();
		}

		if (instance instanceof ASTNode) {
			ASTNode node = (ASTNode) instance;
			return ASTNode.nodeClassForType(node.getNodeType()).toString();
		}

		return "";
	}

	@Override
	public boolean isOfType(Object instance, String metaClass)
			throws EolModelElementTypeNotFoundException {
		return instance.getClass().getSimpleName().equals(metaClass);
	}

	@Override
	public Object createInstance(String type)
			throws EolModelElementTypeNotFoundException,
			EolNotInstantiableModelElementTypeException {

		return null;
	}

	@Override
	public Object getElementById(String id) {

		return null;
	}

	@Override
	public String getElementId(Object instance) {

		return null;
	}

	@Override
	public void setElementId(Object instance, String newId) {

	}

	@Override
	public void deleteElement(Object instance) throws EolRuntimeException {

	}

	@Override
	public boolean owns(Object instance) {
		if (instance instanceof IJavaElement || instance instanceof ASTNode) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isInstantiable(String type) {
		return true;
	}

	@Override
	public boolean hasType(String type) {
		try {
			return supportedTypes.contains(type)
					|| (Class.forName("org.eclipse.jdt.core.dom." + type) != null);
		} catch (ClassNotFoundException e) {
			return false;
		}
	}

	@Override
	public boolean store(String location) {
		return false;
	}

	@Override
	public boolean store() {
		return false;
	}

	@Override
	public IPropertyGetter getPropertyGetter() {
		return new JdtPropertyGetter();
	}

	private List<IJavaProject> getJavaProject(String[] projectsName) {
		List<IProject> projects;
		if (projectsName == null) {
			// get all projects in the workspace including non-Java projects
			projects = JdtReader.getIProjects();
			System.out.println("projects no.:"+projects.size());
		} else {
			// get selected projects in the workspace
			projects = JdtReader.getIProjects(projectsName);
		}
		try {
			// get java projects
			javaProjects.addAll(JdtReader.getIJavaProjects(projects));
		} catch (CoreException e) {
			e.printStackTrace();
		}
		System.out.println("java projects no.:"+projects.size());
		
		return javaProjects;
	}
}
